import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Polygon;
import java.awt.geom.AffineTransform;
import java.awt.geom.Area;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Rectangle2D;
import java.awt.geom.RoundRectangle2D;
import java.util.ArrayList;

import org.jbox2d.collision.CircleDef;
import org.jbox2d.collision.Shape;
import org.jbox2d.collision.ShapeDef;
import org.jbox2d.common.Vec2;
import org.jbox2d.dynamics.Body;

import pulpcore.Stage;
import pulpcore.animation.Bool;
import pulpcore.animation.Property;
import pulpcore.animation.PropertyListener;
import pulpcore.animation.Timeline;
import pulpcore.animation.event.TimelineEvent;
import pulpcore.scene.Scene2D;
import pulpfizz.physics.Actor;
import pulpfizz.physics.BodyUtils;
import pulpfizz.physics.CollisionUtils;
import pulpfizz.physics.Material;
import pulpfizz.physics.PolygonUtils;
import pulpfizz.pulp.body.BodyDrawer;
import pulpfizz.pulp.body.Java2DBodySprite;
import pulpfizz.pulp.body.PhysicsLayer;

public class Puck extends Actor implements PropertyListener,BodyDrawer
{
	PhysicsLayer physics;
	Body body;
	Shape sensor;
	Area drawArea;
	Bool collideWithStuff;
	Java2DBodySprite sprite;
	boolean scoredYet;
	
	public Puck(PhysicsLayer physics, float size, int index)
	{
		this.physics = physics;
		float puckSize = size / 10f;
		createBody(physics, Pucks.values()[index], puckSize);
		
//		createBody(physics, Pucks.SPIRAL_STAR, puckSize);
		
		body.setBullet(true);
		// Boolean listening.
		collideWithStuff = new Bool(this, true);
		collideWithStuff.set(false);

		CircleDef cd = new CircleDef();
		cd.radius = 0.1f;
		cd.isSensor = true;
		cd.density = 0f;
		sensor = body.createShape(cd);

		sprite = new Java2DBodySprite(body, physics, 2);
		sprite.setBodyDrawer(this);
		sprite.setPadding(2f);
		
		physics.add(sprite);

		// Contact stuff.
		addToGroup(TwoPlayerScene.PUCK_GROUP);
		addBody(body);
	}

	public void destroy()
	{
		sprite.alpha.animate(255, 0, 500);
		TimelineEvent te = new TimelineEvent(500)
		{
			@Override
			public void run()
			{
				body.getWorld().destroyBody(body);
				// TODO: take the image and throw it into the bottom info bar.
				physics.remove(sprite);
				body = null;
				sensor = null;
				physics = null;
			}
		};
		Timeline t = new Timeline();
		t.addEvent(te);
		((Scene2D)Stage.getScene()).addTimeline(t);
	}
	
	public void propertyChange(Property property)
	{
		if (collideWithStuff.get())
		{
			System.out.println("Heyaaa");
			CollisionUtils.setMask(0xf, body);
			CollisionUtils.maskOut(Player.CATEGORY, body);
		} else
		{
			CollisionUtils.setMask(0x0, body);
		}
	}

	public enum Pucks {
		CIRCLE, TRIANGLE, SQUARE, EGG, STAR, STAR_DEEP, DENSE_STAR, TOENAIL, DOUBLE_TOENAIL, SPIRAL_STAR, TOOTHPICK
	}

		public void createBody(PhysicsLayer physics, Pucks type, float size)
		{
			Body b = null;
			Area a = null;

			float density = 1f;
			float centerWeight = 5f;

			if (type == Pucks.CIRCLE)
			{
				density = 0.25f;
				Ellipse2D ellipse = ellipse(0, 0, size*.8f);

				a = new Area(ellipse);
				b = PolygonUtils.areaToBody(physics, a, size / 100);
			} else if (type == Pucks.TRIANGLE)
			{
				Polygon p = new Polygon();
				for (int i = 0; i < 3; i++)
				{
					double dTheta = Math.PI * 2 / 3;
					double x = 100 * size*1.3f * Math.cos(dTheta * i);
					double y = 100 * size*1.3f * Math.sin(dTheta * i);
					p.addPoint((int) x, (int) y);
				}
				a = new Area(p);
				a = a.createTransformedArea(AffineTransform.getScaleInstance(1 / 100f, 1 / 100f));
				b = PolygonUtils.areaToBody(physics, a, size / 100);
			} else if (type == Pucks.SQUARE)
			{
				Polygon p = new Polygon();
				for (int i = 0; i < 4; i++)
				{
					double dTheta = Math.PI * 2 / 4;
					double x = 100 * size*1.2f * Math.cos(dTheta * i);
					double y = 100 * size*1.2f * Math.sin(dTheta * i);
					p.addPoint((int) x, (int) y);
				}
				a = new Area(p);
				a = a.createTransformedArea(AffineTransform.getScaleInstance(1 / 100f, 1 / 100f));
				b = PolygonUtils.areaToBody(physics, a, size / 100);
			} else if (type == Pucks.EGG)
			{
				double rad = Math.sqrt(2) * size;
				Ellipse2D.Float ellipse = new Ellipse2D.Float();
				ellipse.setFrameFromCenter(0, 0, (float) rad, (float) rad * .5f);

				a = new Area(ellipse);
				b = PolygonUtils.areaToBody(physics, a, size / 100);
				density *= .3f;
				centerWeight = 8f;
			} else if (type == Pucks.STAR || type == Pucks.STAR_DEEP || type == Pucks.DENSE_STAR)
			{
				float outerRad = size;
				float innerRad = size * .7f;
				int n = 5;
				if (type == Pucks.STAR_DEEP)
				{
					innerRad = size * .25f;
				} else if (type == Pucks.DENSE_STAR)
				{
					innerRad = size * .5f;
					n = 10;
				}
				Polygon p = new Polygon();
				double dTheta = Math.PI * 2 / n;
				for (int i = 0; i < n; i++)
				{
					p.addPoint((int) (100 * outerRad * Math.cos(i * dTheta)), (int) (100 * outerRad * Math.sin(i
							* dTheta)));
					p.addPoint((int) (100 * innerRad * Math.cos(i * dTheta + dTheta / 2f)),
						(int) (100 * innerRad * Math.sin(i * dTheta + dTheta / 2f)));
				}

				a = new Area(p);
				a = a.createTransformedArea(AffineTransform.getScaleInstance(1 / 100f, 1 / 100f));
				b = PolygonUtils.areaToBody(physics, a, size / 100);
				//				a.add(new Area(p));
			} else if (type == Pucks.TOENAIL)
			{
				Ellipse2D pos = ellipse(0, 0, size);
				Ellipse2D neg = ellipse(0, size * .4f, size);

				a = new Area(pos);
				a.subtract(new Area(neg));
				b = PolygonUtils.areaToBody(physics, a, size / 10);
				centerWeight = -1;
			} else if (type == Pucks.DOUBLE_TOENAIL)
			{
				Ellipse2D pos = ellipse(0, 0, size);
				Ellipse2D neg1 = ellipse(0, size * 1.5f, size * .75f);
				Ellipse2D neg2 = ellipse(0, -size * 1.5f, size * .75f);

				a = new Area(pos);
				a.subtract(new Area(neg1));
				a.subtract(new Area(neg2));
				b = PolygonUtils.areaToBody(physics, a, size / 15);
			} else if (type == Pucks.SPIRAL_STAR)
			{
				Rectangle2D.Float clip = new Rectangle2D.Float(-size, -size, size, size);

				Ellipse2D pos = ellipse(0, 0, size / 2);
				Ellipse2D neg = ellipse(0, -size / 4, size / 4);

				a = new Area(pos);
				a.intersect(new Area(clip));
				a.subtract(new Area(neg));
				a.transform(AffineTransform.getTranslateInstance(.45f * size, -size * .4f));

				Area totalArea = new Area();
				ArrayList<ShapeDef> shapes = new ArrayList<ShapeDef>();
				int n = 3;
				float dTheta = (float) (Math.PI * 2 / n);
				for (int i = 0; i < n; i++)
				{
					Area a2 = (Area) a.clone();
					a2.transform(AffineTransform.getRotateInstance(dTheta * i, 0, 0));
					totalArea.add(a2);
					ShapeDef[] sd = PolygonUtils.areaToShapes(physics, a2, size / 20);
					for (ShapeDef d : sd)
					{
						shapes.add(d);
					}
				}

				Ellipse2D ell = new Ellipse2D.Float(-size*.45f,-size*.45f,size*.45f*2,size*.45f*2);
				totalArea.add(new Area(ell));
				drawArea = totalArea;
				
				b = BodyUtils.createCircle(physics.getWorld(), 0, 0, size * .4f);

				for (ShapeDef d : shapes)
				{
					b.createShape(d);
				}
				centerWeight = 5;
			} else if (type == Pucks.TOOTHPICK)
			{
				b = BodyUtils.createRoundStick(physics.getWorld(), new Vec2(-size*1.5f, 0), new Vec2(size*1.5f, 0), size / 20);
				centerWeight = 10;
				RoundRectangle2D.Float rect = new RoundRectangle2D.Float();
				rect.setRoundRect(-size*1.5f, -size/40, size*3f, size/20, size/20, size/20);
				a = new Area(rect);
			}

			if (drawArea == null)
				drawArea = a;

			// Apply some material.
			Material m = Material.WOOD();
			m.setDensity(m.getDensity() * density);
			m.applyAll(b);

			if (centerWeight >= 0)
			{
				// Always create a heavy center (a la x-fire).
				CircleDef cd = new CircleDef();
				cd.radius = size / 20f;

				// Try and make the center equally heavy.
				double area = Math.PI * cd.radius * cd.radius;
				cd.density = b.getMass() * centerWeight / (float) area;
				Shape s = b.createShape(cd);
				CollisionUtils.maskOut(0x0, s);
				b.setMassFromShapes();
			}

			b.m_linearDamping = 0.5f;
			b.m_angularDamping = 0.1f;

			body = b;
		}

		public static Ellipse2D.Float ellipse(float cx, float cy, float rad)
		{
			float corner = (float) (Math.sqrt(2) * rad);
			Ellipse2D.Float ellipse = new Ellipse2D.Float();
			ellipse.setFrameFromCenter(cx, cy, (float) cx + corner, (float) cy + corner);
			return ellipse;
		}

	public void drawBody(PhysicsLayer physics, Body b, Graphics2D g2)
	{
		g2.setColor(new Color(0,255,0));
		
		g2.setStroke(new BasicStroke(1 / physics.getScaling()*2));
		
		g2.draw(drawArea);
	}

	public boolean needsRedraw()
	{
		return false;
	}

}
